﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.MathLib.CSG
{
   class Empty3d
    {
        const double SMALL_NUM = 1.0e-8; // to avoid division overflow

        public class TriIn
        {
            public Vec3[] p = new Vec3[3];
        }
        public class EdgeIn
        {
            public Vec3[] p = new Vec3[2];
        }
       public class TriEdgeIn
        {
            public TriIn tri = new TriIn();
            public EdgeIn edge = new EdgeIn();
        }
        public class TriTriTriIn
        {
            public TriIn[] tri = new TriIn[] { new TriIn(),new TriIn(),new TriIn() };
        }



       public static int degeneracy_count; // count degeneracies encountered
        int exact_count; // count of filter calls failed
        int callcount; // total call count

        Quantization quantizer;

        public Empty3d(Quantization _quantizer)
        {
            //               : degeneracy_count(0)
            //, exact_count(0)
            //, callcount(0)
            //, quantizer(_quantizer)
        }
        public bool isEmpty(TriEdgeIn input)
        {
            return false;
        }

        public Vec3 coords(TriEdgeIn input)
        {
            return null;
        }
        public bool emptyExact(TriEdgeIn input)
        {
            return false;
        }
        public Vec3 coordsExact(TriEdgeIn input)
        {
            return null;

        }

        // Determines if an edge intersects a triangle
        //    Input:  an edge and a triangle
        //    Return: false if the edge intersects the triangle
        public bool emptyApprox(TriEdgeIn input)
        {
            callcount++;

            // get triangle edge vectors and plane normal
            Vec3 u = input.tri.p[1] - input.tri.p[0];
            Vec3 v = input.tri.p[2] - input.tri.p[0];
            Vec3 n = Vec3.cross(u, v);          // cross product
            if (Vec3.abs(n).Max() == 0)           // triangle is degenerate
            {
                degeneracy_count++;
                return true;                    // do not deal with this case
            }

            Vec3 dir = input.edge.p[1] - input.edge.p[0];          // edge 'direction' vector
            Vec3 w0 = input.edge.p[0] - input.tri.p[0];
            double a = -Vec3.dot(n, w0);
            double b = Vec3.dot(n, dir);

            if (Math.Abs(b) < SMALL_NUM)        // edge is parallel to triangle plane
            {
                /*if (a == 0)					// edge lies in triangle plane
                {
                    //TODO: we still want to test the intersection?!
                    if (intersect3D_EdgeEdge(input.edge.p[0],input.edge.p[1],input.tri.p[0],input.tri.p[1]))
                        return false;
                    if (intersect3D_EdgeEdge(input.edge.p[0],input.edge.p[1],input.tri.p[1],input.tri.p[2]))
                        return false;
                    if (intersect3D_EdgeEdge(v,input.edge.p[1],input.tri.p[2],input.tri.p[0]))
                        return false;
                }
                //*/
                return true;                // edge disjoint from plane
            }

            // get intersect point of edge with triangle plane
            double r = a / b;
            if (r < SMALL_NUM || r > 1.0 - SMALL_NUM)           // edge doesn't realy intersect the triangle plane
                return true;                    // => no intersect

            Vec3 I = input.edge.p[0] + r * dir;            // intersect point of ray and plane

            // is I inside T?
            double uu, uv, vv, wu, wv, D;
            uu = Vec3.dot(u, u);
            uv = Vec3.dot(u, v);
            vv = Vec3.dot(v, v);
            Vec3 w = I - input.tri.p[0];
            wu = Vec3.dot(w, u);
            wv = Vec3.dot(w, v);
            D = uv * uv - uu * vv;

            // get and test parametric coords
            double s, t;
            s = (uv * wv - vv * wu) / D;
            if (s < 0.0 || s > 1.0)         // I is outside T
                return true;
            t = (uv * wu - uu * wv) / D;
            if (t < 0.0 || (s + t) > 1.0)   // I is outside T
                return true;

            return false;					// I is in T
        }
        public Vec3 coordsApprox(TriEdgeIn input)
        {
            // get triangle edge vectors and plane normal
            Vec3 u = input.tri.p[1] - input.tri.p[0];
            Vec3 v = input.tri.p[2] - input.tri.p[0];
            Vec3 n = Vec3.cross(u, v);          // cross product
            Utils.ENSURE(Vec3.abs(n).Max() != 0);

            Vec3 dir = input.edge.p[1] - input.edge.p[0];          // edge 'direction' vector
            Vec3 w0 = input.edge.p[0] - input.tri.p[0];
            double a = -Vec3.dot(n, w0);
            double b = Vec3.dot(n, dir);
            Utils.ENSURE(Math.Abs(b) >= SMALL_NUM);

            // get intersect point of edge with triangle plane
            double r = a / b;
            Utils.ENSURE(r >= SMALL_NUM && r <= 1.0 - SMALL_NUM);

            Vec3 I = input.edge.p[0] + r * dir;            // intersect point of ray and plane
            return I;

        }


        public bool isEmpty(TriTriTriIn input)
        {
            return false;

        }
        public Vec3 coords(TriTriTriIn input)
        {
            return null;
        }
        public bool emptyExact(TriTriTriIn input)
        {
            return false;

        }
        public Vec3 coordsExact(TriTriTriIn input)
        {
            return null;

        }

        public bool hasDegeneracies()
        {
            return degeneracy_count > 0;
        }

        public void resetCounts()
        {
            degeneracy_count = 0;
            exact_count = 0;
            callcount = 0;
        }

        /*
        // exact versions


        bool emptyExact(const Cell3d0 &c0,
        const Complex3d2 &complex,
        const Metric3d2 &metric);

        void cell3d0toPointExact(SmVector3 &point,
        const Cell3d0 &c0,
        const Complex3d2 &complex,
        const Metric3d2 &metric);
        */



        protected int emptyFilter(TriEdgeIn input)
        {
            return 0;
        }
        protected bool exactFallback(TriEdgeIn input)
        {
            return false;
        }

        protected int emptyFilter(TriTriTriIn input)
        {
            return 0;
        }
        protected bool exactFallback(TriTriTriIn input)
        {
            return false;
        }

    }// end class Empty3d
}
